Completed
Push — patch_1-1-4 ( 3f780f...826343 )
by Emanuele
25:17 queued 11:40
created

smc_AutoSuggest.autoSuggestHide   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 5
rs 10
c 0
b 0
f 0
1
/*!
2
 * @name      ElkArte Forum
3
 * @copyright ElkArte Forum contributors
4
 * @license   BSD http://opensource.org/licenses/BSD-3-Clause
5
 *
6
 * This file contains code covered by:
7
 * copyright:	2011 Simple Machines (http://www.simplemachines.org)
8
 * license:		BSD, See included LICENSE.TXT for terms and conditions.
9
 *
10
 * @version 1.1
11
 */
12
13
/**
14
 * This file contains javascript associated with a autosuggest control.
15
 */
16
17
/**
18
 * The autosuggest class, used to display a selection list of members
19
 *
20
 * @param {object} oOptions
21
 */
22
function smc_AutoSuggest(oOptions)
23
{
24
	this.opt = oOptions;
25
26
	// Store the handle to the text box.
27
	this.oTextHandle = document.getElementById(this.opt.sControlId);
28
	this.oRealTextHandle = null;
29
	this.oSuggestDivHandle = null;
30
31
	this.sLastSearch = '';
32
	this.sLastDirtySearch = '';
33
	this.oSelectedDiv = null;
34
	this.aCache = [];
35
	this.aDisplayData = [];
36
	this.sRetrieveURL = 'sRetrieveURL' in this.opt ? this.opt.sRetrieveURL : '%scripturl%action=suggest;xml';
37
38
	// How many objects can we show at once?
39
	this.iMaxDisplayQuantity = 'iMaxDisplayQuantity' in this.opt ? this.opt.iMaxDisplayQuantity : 12;
40
41
	// How many characters shall we start searching on?
42
	this.iMinimumSearchChars = 'iMinimumSearchChars' in this.opt ? this.opt.iMinimumSearchChars : 2;
43
44
	// Should selected items be added to a list?
45
	this.bItemList = 'bItemList' in this.opt ? this.opt.bItemList : false;
46
47
	// Are there any items that should be added in advance?
48
	this.aListItems = 'aListItems' in this.opt ? this.opt.aListItems : [];
49
50
	this.sItemTemplate = 'sItemTemplate' in this.opt ? this.opt.sItemTemplate : '<input type="hidden" name="%post_name%[]" value="%item_id%" /><a href="%item_href%" class="extern" onclick="window.open(this.href, \'_blank\'); return false;">%item_name%</a>&nbsp;<img src="%images_url%/pm_recipient_delete.png" alt="%delete_text%" title="%delete_text%" onclick="return %self%.deleteAddedItem(%item_id%)" />';
51
	this.sTextDeleteItem = 'sTextDeleteItem' in this.opt ? this.opt.sTextDeleteItem : '';
52
	this.oCallback = {};
53
	this.bDoAutoAdd = false;
54
	this.iItemCount = 0;
55
	this.oHideTimer = null;
56
	this.bPositionComplete = false;
57
58
	this.oXmlRequestHandle = null;
59
60
	// Just make sure the page is loaded before calling the init.
61
	addLoadEvent(this.opt.sSelf + '.init();');
62
}
63
64
// Initialize our autosuggest object, adds events and containers to the element we monitor
65
smc_AutoSuggest.prototype.init = function()
66
{
67
	// Create a div that'll contain the results later on.
68
	this.oSuggestDivHandle = document.createElement('div');
69
	this.oSuggestDivHandle.className = 'auto_suggest_div';
70
	document.body.appendChild(this.oSuggestDivHandle);
71
72
	// Create a backup text input.
73
	this.oRealTextHandle = document.createElement('input');
74
	this.oRealTextHandle.type = 'hidden';
75
	this.oRealTextHandle.name = this.oTextHandle.name;
76
	this.oRealTextHandle.value = this.oTextHandle.value;
77
	this.oTextHandle.form.appendChild(this.oRealTextHandle);
78
79
	// Disable autocomplete in any browser by obfuscating the name.
80
	this.oTextHandle.name = 'dummy_' + Math.floor(Math.random() * 1000000);
81
	this.oTextHandle.autocomplete = 'off';
82
83
	this.oTextHandle.instanceRef = this;
84
85
	// Set up all the event monitoring
86
	this.oTextHandle.onkeydown = function (oEvent) {return this.instanceRef.handleKey(oEvent);};
87
	this.oTextHandle.onkeyup = function (oEvent) {return this.instanceRef.autoSuggestUpdate(oEvent);};
88
	this.oTextHandle.onchange = function (oEvent) {return this.instanceRef.autoSuggestUpdate(oEvent);};
89
	this.oTextHandle.onblur = function (oEvent) {return this.instanceRef.autoSuggestHide(oEvent);};
90
	this.oTextHandle.onfocus = function (oEvent) {return this.instanceRef.autoSuggestUpdate(oEvent);};
91
92
	// Adding items to a list, then we need a place to insert them
93
	if (this.bItemList)
94
	{
95
		if ('sItemListContainerId' in this.opt)
96
			this.oItemList = document.getElementById(this.opt.sItemListContainerId);
97
		else
98
		{
99
			this.oItemList = document.createElement('div');
100
			this.oTextHandle.parentNode.insertBefore(this.oItemList, this.oTextHandle.nextSibling);
101
		}
102
	}
103
104
	// Items provided to add to the top of the selection list?
105
	if (this.aListItems.length > 0)
106
		for (var i = 0, n = this.aListItems.length; i < n; i++)
107
			this.addItemLink(this.aListItems[i].sItemId, this.aListItems[i].sItemName);
108
109
	return true;
110
};
111
112
/**
113
 * Handle keypress events for the suggest controller
114
 *
115
 * @param oEvent
116
 */
117
smc_AutoSuggest.prototype.handleKey = function(oEvent)
118
{
119
	// Grab the event object, one way or the other
120
	if (!oEvent)
121
		oEvent = window.event;
122
123
	// Get the keycode of the key that was pressed.
124
	var iKeyPress = 0;
125
	if ('which' in oEvent)
126
		iKeyPress = oEvent.which;
127
	else if ('keyCode' in oEvent)
128
		iKeyPress = oEvent.keyCode;
129
130
	// Check what key they have pressed
131
	switch (iKeyPress)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
132
	{
133
		// Tab.
134
		case 9:
135
			if (this.aDisplayData.length > 0)
136
			{
137
				if (this.oSelectedDiv !== null)
138
					this.itemClicked(this.oSelectedDiv);
139
				else
140
					this.handleSubmit();
141
			}
142
143
			// Continue to the next control.
144
			return true;
145
		break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
Unused Code introduced by
The code break after return is not reachable.
Loading history...
146
147
		// Was it an Enter key - if so assume they are trying to select something.
148
		case 13:
149
			if (this.aDisplayData.length > 0 && this.oSelectedDiv !== null)
150
			{
151
				this.itemClicked(this.oSelectedDiv);
152
153
				// Do our best to stop it submitting the form!
154
				return false;
155
			}
156
			else
157
				return true;
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
158
		break;
0 ignored issues
show
Unused Code introduced by
This break statement is unnecessary and may be removed.
Loading history...
159
160
		// Up/Down arrow?
161
		case 38:
162
		case 40:
163
			if (this.aDisplayData.length && this.oSuggestDivHandle.style.visibility !== 'hidden')
164
			{
165
				// Loop through the display data trying to find our entry.
166
				var bPrevHandle = false,
167
					oToHighlight = null;
168
169
				for (var i = 0; i < this.aDisplayData.length; i++)
170
				{
171
					// If we're going up and yet the top one was already selected don't go around.
172
					if (this.oSelectedDiv !== null && this.oSelectedDiv === this.aDisplayData[i] && i === 0 && iKeyPress === 38)
173
					{
174
						oToHighlight = this.oSelectedDiv;
175
						break;
176
					}
177
178
					// If nothing is selected and we are going down then we select the first one.
179
					if (this.oSelectedDiv === null && iKeyPress === 40)
180
					{
181
						oToHighlight = this.aDisplayData[i];
182
						break;
183
					}
184
185
					// If the previous handle was the actual previously selected one and we're hitting down then this is the one we want.
186
					if (bPrevHandle !== false && bPrevHandle === this.oSelectedDiv && iKeyPress === 40)
187
					{
188
						oToHighlight = this.aDisplayData[i];
189
						break;
190
					}
191
192
					// If we're going up and this is the previously selected one then we want the one before, if there was one.
193
					if (bPrevHandle !== false && this.aDisplayData[i] === this.oSelectedDiv && iKeyPress === 38)
194
					{
195
						oToHighlight = bPrevHandle;
196
						break;
197
					}
198
199
					// Make the previous handle this!
200
					bPrevHandle = this.aDisplayData[i];
201
				}
202
203
				// If we don't have one to highlight by now then it must be the last one that we're after.
204
				if (oToHighlight === null)
205
					oToHighlight = bPrevHandle;
206
207
				// Remove any old highlighting.
208
				if (this.oSelectedDiv !== null)
209
					this.itemMouseOut(this.oSelectedDiv);
210
211
				// Mark what the selected div now is.
212
				this.oSelectedDiv = oToHighlight;
213
				this.itemMouseOver(this.oSelectedDiv);
214
			}
215
		break;
216
	}
217
	return true;
218
};
219
220
// Functions for integration.
221
smc_AutoSuggest.prototype.registerCallback = function(sCallbackType, sCallback)
222
{
223
	switch (sCallbackType)
0 ignored issues
show
Coding Style introduced by
As per coding-style, switch statements should have a default case.
Loading history...
224
	{
225
		case 'onBeforeAddItem':
226
			this.oCallback.onBeforeAddItem = sCallback;
227
		break;
228
229
		case 'onAfterAddItem':
230
			this.oCallback.onAfterAddItem = sCallback;
231
		break;
232
233
		case 'onAfterDeleteItem':
234
			this.oCallback.onAfterDeleteItem = sCallback;
235
		break;
236
237
		case 'onBeforeUpdate':
238
			this.oCallback.onBeforeUpdate = sCallback;
239
		break;
240
	}
241
};
242
243
// User hit submit?
244
smc_AutoSuggest.prototype.handleSubmit = function()
245
{
246
	var bReturnValue = true,
247
		oFoundEntry = null;
248
249
	// Do we have something that matches the current text?
250
	for (var i = 0; i < this.aCache.length; i++)
251
	{
252
		if (this.sLastSearch.toLowerCase() === this.aCache[i].sItemName.toLowerCase().substr(0, this.sLastSearch.length))
253
		{
254
			// Exact match?
255
			if (this.sLastSearch.length === this.aCache[i].sItemName.length)
256
			{
257
				// This is the one!
258
				oFoundEntry = {
259
					sItemId: this.aCache[i].sItemId,
260
					sItemName: this.aCache[i].sItemName
261
				};
262
263
				break;
264
			}
265
			// Not an exact match, but it'll do for now.
266
			else
267
			{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
268
				// If we have two matches don't find anything.
269
				if (oFoundEntry !== null)
270
					bReturnValue = false;
271
				else {
272
					oFoundEntry = {
273
						sItemId: this.aCache[i].sItemId,
274
						sItemName: this.aCache[i].sItemName
275
					};
276
				}
277
			}
278
		}
279
	}
280
281
	if (oFoundEntry === null || bReturnValue === false)
282
		return bReturnValue;
283
	else
284
	{
0 ignored issues
show
Comprehensibility introduced by
else is not necessary here since all if branches return, consider removing it to reduce nesting and make code more readable.
Loading history...
285
		this.addItemLink(oFoundEntry.sItemId, oFoundEntry.sItemName, true);
286
		return false;
287
	}
288
};
289
290
// Positions the box correctly on the window.
291
smc_AutoSuggest.prototype.positionDiv = function()
292
{
293
	// Only do it once.
294
	if (this.bPositionComplete)
295
		return true;
296
297
	this.bPositionComplete = true;
298
299
	// Put the div under the text box.
300
	var aParentPos = elk_itemPos(this.oTextHandle);
301
302
	this.oSuggestDivHandle.style.left = aParentPos[0] + 'px';
303
	this.oSuggestDivHandle.style.top = (aParentPos[1] + this.oTextHandle.offsetHeight) + 'px';
304
	this.oSuggestDivHandle.style.width = this.oTextHandle.clientWidth + 'px';
305
306
	return true;
307
};
308
309
// Do something after clicking an item.
310
smc_AutoSuggest.prototype.itemClicked = function(oCurElement)
311
{
312
	// Is there a div that we are populating?
313
	if (this.bItemList)
314
		this.addItemLink(oCurElement.sItemId, oCurElement.innerHTML);
315
	// Otherwise clear things down.
316
	else
317
		this.oTextHandle.value = oCurElement.innerHTML.php_unhtmlspecialchars();
318
319
	this.oRealTextHandle.value = this.oTextHandle.value;
320
	this.autoSuggestActualHide();
321
	this.oSelectedDiv = null;
322
};
323
324
// Remove the last searched for name from the search box.
325
smc_AutoSuggest.prototype.removeLastSearchString = function ()
326
{
327
	// Remove the text we searched for from the div.
328
	var sTempText = this.oTextHandle.value.toLowerCase(),
329
		iStartString = sTempText.indexOf(this.sLastSearch.toLowerCase());
330
331
	// Just attempt to remove the bits we just searched for.
332
	if (iStartString !== -1)
333
	{
334
		while (iStartString > 0)
335
		{
336
			if (sTempText.charAt(iStartString - 1) === '"' || sTempText.charAt(iStartString - 1) === ',' || sTempText.charAt(iStartString - 1) === ' ')
337
			{
338
				iStartString--;
339
				if (sTempText.charAt(iStartString - 1) === ',')
340
					break;
341
			}
342
			else
343
				break;
344
		}
345
346
		// Now remove anything from iStartString upwards.
347
		this.oTextHandle.value = this.oTextHandle.value.substr(0, iStartString);
348
	}
349
	// Just take it all.
350
	else
351
		this.oTextHandle.value = '';
352
};
353
354
// Add a result if not already done.
355
smc_AutoSuggest.prototype.addItemLink = function (sItemId, sItemName, bFromSubmit)
356
{
357
	// Increase the internal item count.
358
	this.iItemCount ++;
359
360
	// If there's a callback then call it.
361
	if ('oCallback' in this && 'onBeforeAddItem' in this.oCallback && typeof(this.oCallback.onBeforeAddItem) === 'string')
362
	{
363
		// If it returns false the item must not be added.
364
		if (!eval(this.oCallback.onBeforeAddItem + '(' + this.opt.sSelf + ', \'' + sItemId + '\');'))
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
Coding Style Security introduced by
The use of eval is generally not recommended.

The use of eval is discouraged because it can potentially make your code vulnerable to various injection attacks.

Besides it will also prevent certain optimizations that runtimes otherwise could make.

Loading history...
365
			return;
366
	}
367
368
	var oNewDiv = document.createElement('div');
369
	oNewDiv.id = 'suggest_' + this.opt.sSuggestId + '_' + sItemId;
370
	oNewDiv.innerHTML = this.sItemTemplate.replace(/%post_name%/g, this.opt.sPostName).replace(/%item_id%/g, sItemId).replace(/%item_href%/g, elk_prepareScriptUrl(elk_scripturl) + this.opt.sURLMask.replace(/%item_id%/g, sItemId)).replace(/%item_name%/g, sItemName).replace(/%images_url%/g, elk_images_url).replace(/%self%/g, this.opt.sSelf).replace(/%delete_text%/g, this.sTextDeleteItem);
0 ignored issues
show
Bug introduced by
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable elk_images_url seems to be never declared. If this is a global, consider adding a /** global: elk_images_url */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
371
	this.oItemList.appendChild(oNewDiv);
372
373
	// If there's a registered callback, call it.
374
	if ('oCallback' in this && 'onAfterAddItem' in this.oCallback && typeof(this.oCallback.onAfterAddItem) === 'string')
375
		eval(this.oCallback.onAfterAddItem + '(' + this.opt.sSelf + ', \'' + oNewDiv.id + '\', ' + this.iItemCount + ');');
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
Coding Style Security introduced by
The use of eval is generally not recommended.

The use of eval is discouraged because it can potentially make your code vulnerable to various injection attacks.

Besides it will also prevent certain optimizations that runtimes otherwise could make.

Loading history...
376
377
	// Clear the div a bit.
378
	this.removeLastSearchString();
379
380
	// If we came from a submit, and there's still more to go, turn on auto add for all the other things.
381
	this.bDoAutoAdd = this.oTextHandle.value !== '' && bFromSubmit;
382
383
	// Update the fellow..
384
	this.autoSuggestUpdate();
385
};
386
387
// Delete an item that has been added, if at all?
388
smc_AutoSuggest.prototype.deleteAddedItem = function (sItemId)
389
{
390
	var oDiv = document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId);
391
392
	// Remove the div if it exists.
393
	if (typeof(oDiv) === 'object' && oDiv !== null)
394
	{
395
		oDiv.parentNode.removeChild(document.getElementById('suggest_' + this.opt.sSuggestId + '_' + sItemId));
396
397
		// Decrease the internal item count.
398
		this.iItemCount --;
399
400
		// If there's a registered callback, call it.
401
		if ('oCallback' in this && 'onAfterDeleteItem' in this.oCallback && typeof(this.oCallback.onAfterDeleteItem) === 'string')
402
			eval(this.oCallback.onAfterDeleteItem + '(' + this.opt.sSelf + ', ' + this.iItemCount + ');');
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
Coding Style Security introduced by
The use of eval is generally not recommended.

The use of eval is discouraged because it can potentially make your code vulnerable to various injection attacks.

Besides it will also prevent certain optimizations that runtimes otherwise could make.

Loading history...
403
	}
404
405
	return false;
406
};
407
408
// Hide the box.
409
smc_AutoSuggest.prototype.autoSuggestHide = function ()
410
{
411
	// Delay to allow events to propagate through....
412
	this.oHideTimer = setTimeout(this.opt.sSelf + '.autoSuggestActualHide();', 250);
413
};
414
415
// Do the actual hiding after a timeout.
416
smc_AutoSuggest.prototype.autoSuggestActualHide = function()
417
{
418
	this.oSuggestDivHandle.style.display = 'none';
419
	this.oSuggestDivHandle.style.visibility = 'hidden';
420
	this.oSelectedDiv = null;
421
};
422
423
// Show the box.
424
smc_AutoSuggest.prototype.autoSuggestShow = function()
425
{
426
	if (this.oHideTimer)
427
	{
428
		clearTimeout(this.oHideTimer);
429
		this.oHideTimer = false;
430
	}
431
432
	this.positionDiv();
433
	this.oSuggestDivHandle.style.visibility = 'visible';
434
	this.oSuggestDivHandle.style.display = '';
435
};
436
437
// Populate the actual div.
438
smc_AutoSuggest.prototype.populateDiv = function(aResults)
439
{
440
	// Cannot have any children yet.
441
	while (this.oSuggestDivHandle.childNodes.length > 0)
442
	{
443
		// Tidy up the events etc too.
444
		this.oSuggestDivHandle.childNodes[0].onmouseover = null;
445
		this.oSuggestDivHandle.childNodes[0].onmouseout = null;
446
		this.oSuggestDivHandle.childNodes[0].onclick = null;
447
448
		this.oSuggestDivHandle.removeChild(this.oSuggestDivHandle.childNodes[0]);
449
	}
450
451
	// Something to display?
452
	if (typeof(aResults) === 'undefined')
453
	{
454
		this.aDisplayData = [];
455
		return false;
456
	}
457
458
	var aNewDisplayData = [];
459
	for (var i = 0; i < (aResults.length > this.iMaxDisplayQuantity ? this.iMaxDisplayQuantity : aResults.length); i++)
460
	{
461
		// Create the sub element
462
		var oNewDivHandle = document.createElement('div');
463
464
		oNewDivHandle.sItemId = aResults[i].sItemId;
465
		oNewDivHandle.className = 'auto_suggest_item';
466
		oNewDivHandle.innerHTML = aResults[i].sItemName;
467
		//oNewDivHandle.style.width = this.oTextHandle.style.width;
468
469
		this.oSuggestDivHandle.appendChild(oNewDivHandle);
470
471
		// Attach some events to it so we can do stuff.
472
		oNewDivHandle.instanceRef = this;
473
		oNewDivHandle.onmouseover = function (oEvent) {this.instanceRef.itemMouseOver(this);};
0 ignored issues
show
Unused Code introduced by
The parameter oEvent is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
474
		oNewDivHandle.onmouseout = function (oEvent) {this.instanceRef.itemMouseOut(this);};
0 ignored issues
show
Unused Code introduced by
The parameter oEvent is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
475
		oNewDivHandle.onclick = function (oEvent) {this.instanceRef.itemClicked(this);};
0 ignored issues
show
Unused Code introduced by
The parameter oEvent is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
476
477
		aNewDisplayData[i] = oNewDivHandle;
478
	}
479
480
	this.aDisplayData = aNewDisplayData;
481
482
	return true;
483
};
484
485
// Refocus the element, set the class name on mouse over
486
smc_AutoSuggest.prototype.itemMouseOver = function (oCurElement)
487
{
488
	this.oSelectedDiv = oCurElement;
489
	oCurElement.className = 'auto_suggest_item_hover';
490
};
491
492
// Onfocus the element, set the classname back on mouse out
493
smc_AutoSuggest.prototype.itemMouseOut = function (oCurElement)
494
{
495
	oCurElement.className = 'auto_suggest_item';
496
};
497
498
// Callback function for the XML request, should contain the list of users that match
499
smc_AutoSuggest.prototype.onSuggestionReceived = function (oXMLDoc)
500
{
501
	var sQuoteText = '',
0 ignored issues
show
Unused Code introduced by
The variable sQuoteText seems to be never used. Consider removing it.
Loading history...
502
		aItems = oXMLDoc.getElementsByTagName('item');
503
504
	// Go through each item received
505
	this.aCache = [];
506
	for (var i = 0; i < aItems.length; i++)
507
	{
508
		this.aCache[i] = {
509
			sItemId: aItems[i].getAttribute('id'),
510
			sItemName: aItems[i].childNodes[0].nodeValue
511
		};
512
513
		// If we're doing auto add and we found the exact person, then add them!
514
		if (this.bDoAutoAdd && this.sLastSearch === this.aCache[i].sItemName)
515
		{
516
			var oReturnValue = {
517
				sItemId: this.aCache[i].sItemId,
518
				sItemName: this.aCache[i].sItemName
519
			};
520
			this.aCache = [];
521
			return this.addItemLink(oReturnValue.sItemId, oReturnValue.sItemName, true);
522
		}
523
	}
524
525
	// Check we don't try to keep auto updating!
526
	this.bDoAutoAdd = false;
527
528
	// Populate the div.
529
	this.populateDiv(this.aCache);
530
531
	// Make sure we can see it - if we can.
532
	if (aItems.length === 0)
533
		this.autoSuggestHide();
534
	else
535
		this.autoSuggestShow();
536
537
	return true;
538
};
539
540
// Get a new suggestion.
541
smc_AutoSuggest.prototype.autoSuggestUpdate = function ()
542
{
543
	// If there's a callback then call it.
544
	if ('onBeforeUpdate' in this.oCallback && typeof(this.oCallback.onBeforeUpdate) === 'string')
545
	{
546
		// If it returns false the item must not be added.
547
		if (!eval(this.oCallback.onBeforeUpdate + '(' + this.opt.sSelf + ');'))
0 ignored issues
show
Security Performance introduced by
Calls to eval are slow and potentially dangerous, especially on untrusted code. Please consider whether there is another way to achieve your goal.
Loading history...
Coding Style Security introduced by
The use of eval is generally not recommended.

The use of eval is discouraged because it can potentially make your code vulnerable to various injection attacks.

Besides it will also prevent certain optimizations that runtimes otherwise could make.

Loading history...
548
			return false;
549
	}
550
551
	this.oRealTextHandle.value = this.oTextHandle.value;
552
553
	if (isEmptyText(this.oTextHandle))
554
	{
555
		this.aCache = [];
556
		this.populateDiv();
557
		this.autoSuggestHide();
558
559
		return true;
560
	}
561
562
	// Nothing changed?
563
	if (this.oTextHandle.value === this.sLastDirtySearch)
564
		return true;
565
	this.sLastDirtySearch = this.oTextHandle.value;
566
567
	// We're only actually interested in the last string.
568
	var sSearchString = this.oTextHandle.value.replace(/^("[^"]+",[ ]*)+/, '').replace(/^([^,]+,[ ]*)+/, '');
569
	if (sSearchString.substr(0, 1) === '"')
570
		sSearchString = sSearchString.substr(1);
571
572
	// Stop replication ASAP.
573
	var sRealLastSearch = this.sLastSearch;
574
	this.sLastSearch = sSearchString;
575
576
	// Either nothing or we've completed a sentence.
577
	if (sSearchString === '' || sSearchString.substr(sSearchString.length - 1) === '"')
578
	{
579
		this.populateDiv();
580
		return true;
581
	}
582
583
	// Nothing?
584
	if (sRealLastSearch === sSearchString)
585
		return true;
586
	// Too small?
587
	else if (sSearchString.length < this.iMinimumSearchChars)
588
	{
589
		this.aCache = [];
590
		this.autoSuggestHide();
591
		return true;
592
	}
593
	else if (sSearchString.substr(0, sRealLastSearch.length) === sRealLastSearch)
594
	{
595
		// Instead of hitting the server again, just narrow down the results...
596
		var aNewCache = [],
597
			j = 0,
598
			sLowercaseSearch = sSearchString.toLowerCase();
599
600
		for (var k = 0; k < this.aCache.length; k++)
601
		{
602
			if (this.aCache[k].sItemName.substr(0, sSearchString.length).toLowerCase() === sLowercaseSearch)
603
				aNewCache[j++] = this.aCache[k];
604
		}
605
606
		this.aCache = [];
607
		if (aNewCache.length !== 0)
608
		{
609
			this.aCache = aNewCache;
610
611
			// Repopulate.
612
			this.populateDiv(this.aCache);
613
614
			// Check it can be seen.
615
			this.autoSuggestShow();
616
617
			return true;
618
		}
619
	}
620
621
	// In progress means destroy!
622
	if (typeof(this.oXmlRequestHandle) === 'object' && this.oXmlRequestHandle !== null)
623
		this.oXmlRequestHandle.abort();
624
625
	// Clean the text handle.
626
	sSearchString = sSearchString.php_urlencode();
627
628
	// Get the document.
629
	var obj = {
630
			"suggest_type": this.opt.sSearchType,
631
			"search": sSearchString,
632
			"time": new Date().getTime()
633
		},
634
		postString;
635
636
	// Post values plus session
637
	postString = "jsonString=" + JSON.stringify(obj) + "&" + this.opt.sSessionVar + "=" + this.opt.sSessionId;
638
639
	sendXMLDocument.call(this, this.sRetrieveURL.replace(/%scripturl%/g, elk_prepareScriptUrl(elk_scripturl)), postString, this.onSuggestionReceived);
0 ignored issues
show
Bug introduced by
The variable elk_scripturl seems to be never declared. If this is a global, consider adding a /** global: elk_scripturl */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
Bug introduced by
The variable sendXMLDocument seems to be never declared. If this is a global, consider adding a /** global: sendXMLDocument */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
640
641
	return true;
642
};